CSCI E-92: Application Note 10 How to Determine the Cause of Execution Branching to the Default ISR Handler ---------------------------------------------------------------------------- In the GCC Toolchain, the Default_Handler ISR (Interrupt Service Routine) is a procedure defined in kinetis_sysinit.c. It is defined as: void Default_Handler() { __asm("bkpt"); } This implies that if it is called, CodeWarrior will pause execution in the debugger as if a breakpoint were reached. In the GCC Toolchain, by default and unless other defined, Default_Handler is established in the Interrupt Vector Table as the ISR for the following ARM Core System interrupts: Non-Maskable Interrupt (NMI) Hard Fault MemManage Fault Bus Fault Usage Fault SuperVisor Call (SVCall) Debug Monitor Pendable request for system service (PendSV) System Tick Timer (SysTick) In the Freescale Toolchain, the isr_default ISR (Interrupt Service Routine) is a procedure defined in kinetis_sysinit.c. It is defined as: void isr_default(void) { /* Write your interrupt code here ... */ } When isr_default is called, CodeWarrior will pause execution in the debugger as if a breakpoint were reached. In the Freescale Toolchain, by default and unless other defined, isr_default is established in the Interrupt Vector Table as the ISR for all interrupts except for non-maskable interrupts. A similarly defined but separate ISR named isrINT_NMI is established for non-maskable interrupts. Once execution has brought control into the default ISR handler, here are actions you can take to determine the cause of the fault. Eclipse should already be running in the Debug Perspective. o Examine the "Debug view"/call stack/traceback pane to see if it shows the procedure that called the default ISR handler. This may be sufficient to allow you to determine the cause of the fault. o Try to set a breakpoint before the fault occurs. If you are able to do so, then single-step the program to locate the point at which the fault occurs. o In the "View stack" pane, select the Registers tab. Expand the "User/System Mode Register" and click on the XPSR. The lower half of the "View stack" pane will now display the bit fields in the XPSR. Scroll down in the lower half of the "View stack" pane to see the fields in the XPSR. As shown in the ARMŪv7-M Architecture Reference Manual, ARM DDI 0403Derrata 2010_Q3 (ID100710) in section B1.4.2 on page B1-624, the exception number field (bits 8-0) holds the exception number of the currently-executing exception. As shown in the ARMŪv7-M Architecture Reference Manual, ARM DDI 0403Derrata 2010_Q3 (ID100710) in section B1.5.2 on page B1-633, table B1-4 shows the defined exception numbers. Exception number Meaning 1 Reset 2 NMI 3 HardFault 4 MemManage 5 BusFault 6 UsageFault 7-10 Reserved 11 SVCall 12 Debug Monitor 13 Reserved 14 PendSV 15 SysTick 16 External interrupt 0 16+n External interrupt n The LR (Link Register) will contain the return address of the default ISR handler routine (i.e., the address where the fault occurred). If the high-order nibble of LR is equal to 0xf, this indicates that the return address is found in a stack frame that was pushed when the exception occurred. The specific values of LR are defined in the ARMŪv7-M Architecture Reference Manual, ARM DDI 0403Derrata 2010_Q3 (ID100710) section B1.5.8 on page B1-652, tables B1-8 and B1-9, as follows: Table B1-8 EXC_RETURN definition of exception return behavior, no FP extension EXC_RETURN Return to Return stack 0xFFFFFFF1 Handler mode Main 0xFFFFFFF9 Thread mode Main 0xFFFFFFFD Thread mode Process Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension EXC_RETURN Return to Return stack Frame type 0xFFFFFFE1 Handler mode Main Extended 0xFFFFFFE9 Thread mode Main Extended 0xFFFFFFED Thread mode Process Extended 0xFFFFFFF1 Handler mode Main Basic 0xFFFFFFF9 Thread mode Main Basic 0xFFFFFFFD Thread mode Process Basic To examine the stack frame, first determine which stack (the main stack or the process stack) was current when the exception occurred. Use the table above to determine the "return stack." In the "View stack" pane, under the "Registers" tab, examine the value of either the SP_MAIN (MSP) or the SP_PROCESS (PSP), as appropriate. Click on the "Memory" tab, then click on the "+" icon. In the "Monitor Memory" pop-up window, enter the value of the appropriate stack pointer (include a "0x" prefix). The stack frame is located starting at the address entered and progressing to higher addresses. The format of the stack frame is defined in our svc.c module. At a byte offset of +20 (decimal) in the stack frame is the saved LR. This is the return address of the program that was running when the exception occurred. The low-order bit of this address is set to indicate that once control is returned, the code should run in "Thumb" instruction execution state. The actual return address, always has the least significant bit cleared. At a byte offset of +24 (decimal) in the stack frame is the saved return address. This is the address where the exception occurred. Once again, the low-order bit of this address is set to indicate that once control is returned, the code should run in "Thumb" instruction execution state. The actual return address, always has the least significant bit cleared. At a byte offset of +28 (decimal) in the stack frame is the saved xPSR. This is the value of the PSR when the exception occurred. In the "View stack" pane under the "Registers" tab, expand the "System Control Registers" and examine the contents of the SCB_SHCSR (System Handler Control and State Register). This register has bits which enable system exceptions and indicates which exceptions are active and pending. The USGFAULTENA, BUSFAULTENA, and MEMFAULTENA bits indicate if the UsageFault, BusFault, and MemManage faults, respectively, are enabled. The reset state of the SCB registers is given in Table B3-4. For the SCB_SHCSR, the reset state is 0; therefore, on reset these faults are disabled. The other bits indicate if exceptions are pending or active. Any fault that is disabled is escalated to a HardFault. In the "View stack" pane under the "Registers" tab, expand the "System Control Registers" and examine the contents of the appropriate fault status register for the fault that occurred. Fault Fault status register ARM Manual MemManage SCB_CFSR (bits 7-0, MMFSR) B3.2.15 BusFault SCB_CFSR (bits 15-8, BFSR) B3.2.15 UsageFault SCB_CFSR (bits 31-16, UFSR) B3.2.15 HardFault SCB_HFSR B3.2.16 Fault Fault address register ARM Manual MemManage SCB_MMFAR B3.2.17 BusFault SCB_BFAR B3.2.18 Here is a sample program built using the GCC Toolchain that includes a run-time bug: int main(void) { __asm("ldr r0,=0"); __asm("ldr r1,=1"); __asm("ldr r2,=2"); __asm("ldr r3,=3"); __asm("ldr r12,=12"); __asm("bx r12"); return 0; } The program initializes registers r0 through r3 and r12 to values 0 through 3 and 12, respectively. It then attempts to branch to the address in register r12. However, this is not a valid operation because the low order bit of register r12 is not set (to indicate "Thumb" instruction execution state). For our processor, "Thumb" instruction execution state is required. When the program is built and run under the debugger, it will enter the debugger in the default ISR handler. The "Debug view"/call stack pane does not shed any information of the point at which the fault occurred. The value of the XPSR is 0x21000003. The exception number is 3 which indicates the exception is a HardFault. LR contains 0xfffffff9 which indicates the existence of a stack frame and that the return stack (and, therefore, the stack frame) is the Main stack (i.e., that the code that faulted was running on the Main stack) and that the fault occurred when running in Thread mode. Examining the MSP (SP_MAIN) shows its value as 0x2000ffd8. We examine memory starting at this address and see the stack frame as containing: *(MSP+0) R0 0 *(MSP+4) R1 1 *(MSP+8) R2 2 *(MSP+12) R3 3 *(MSP+16) R12 0xc *(MSP+20) LR 0x1fff0295 *(MSP+24) RetAdr 0xc *(MSP+28) xPSR 0x20000200 The values for R0-R3 and R12 make sense for our program. The value of LR after the LSB -- the "Thumb" instruction execution state bit -- is cleared is 0x1fff0294. This is the address of the instruction following "bl main" in "__thumb_startup". This makes perfect sense as it is the return address for the main program and, therefore, after the "bl main" instruction is executed this address (with the LSB set) is loaded into the LR register as the return address. The value for RetAdr is 0xc because our program attempted to branch to address 0xc and the PC was set to 0xc before the exception occured. The saved xPSR value of 0x20000200 indicates that the C (Carry) bit is set in the APSR and that in the EPSR some bit is set in one of the ICI/IT (Interrupt-Continue load/store Instructions (ICI) and If-Then (IT) instruction) fields to allow continuation of these instructions on return from an interrupt. This xPSR state is unusual in that the required T (Thumb) bit in the EPSR is *not* set because of our error. The SCB_CFSR (value 0x00020000) has the INVSTATE bit set which, according to the debugger, indicates "the processor has attempted to execute an instruction that makes illegal use of the EPSR." The manual says, "instruction executed with invalid EPSR.T or EPSR.IT field." The SCB_HFSR (value 0x40000000) has the FORCED bit set which, according to the debugger, indicates "forced HardFault." The manual says, "processor has escalated a configurable-priority exception to HardFault." Here is another sample program built using the GCC Toolchain that includes a run-time bug: int main(void) { __asm("ldr r0,=0"); __asm("ldr r1,=1"); __asm("ldr r2,=2"); __asm("ldr r3,=3"); __asm("ldr r12,=12"); __asm("str r12,[r0,r3]"); return 0; } The program initializes registers r0 through r3 and r12 to values 0 through 3 and 12, respectively. It then attempts to store the value in register r12 to a word in memory whose address is the sum of the values of registers r0 and r3. However, this is not a valid operation because that address is not word aligned (the address is 3). When the program is built and run under the debugger, it will enter the debugger in the default ISR handler. The "Debug view"/call stack pane shows that the default ISR handler was called from main.c:7 (meaning line 7) with address 0x1fff01e8. Clicking on the "1 main() main.c:7 0x1fff02bc" line displays the source line in main.c that caused the error and the Disassembly pane shows the address and the assembly code where the error occurred. This might be sufficient to find the error, but let's continue the debugging process. The value of the XPSR is 0x21000003. The exception number is 3 which indicates the exception is a HardFault. LR contains 0xfffffff9 which indicates the existence of a stack frame and that the return stack (and, therefore, the stack frame) is the Main stack (i.e., that the code that faulted was running on the Main stack) and that the fault occurred when running in Thread mode. Examining the MSP (SP_MAIN) shows its value as 0x2000ffd8. We examine memory starting at this address and see the stack frame as containing: *(MSP+0) R0 0 *(MSP+4) R1 1 *(MSP+8) R2 2 *(MSP+12) R3 3 *(MSP+16) R12 0xc *(MSP+20) LR 0x1fff0295 *(MSP+24) RetAdr 0x1fff02bc *(MSP+28) xPSR 0x21000200 The values for R0-R3 and R12 make sense for our program. The value of LR after the LSB -- the "Thumb" instruction execution state bit -- is cleared is 0x1fff0294. This is the address of the instruction following "bl main" in "__thumb_startup". This makes perfect sense as it is the return address for the main program and, therefore, after the "bl main" instruction is executed this address (with the LSB set) is loaded into the LR register as the return address. The value for RetAdr is 0x1fff02bc because that is the PC value where the exception occured. The saved xPSR value of 0x21000200 indicates that the C (Carry) bit is set in the APSR and that in the EPSR the Thumb bit is set and some bit is set in one of the ICI/IT (Interrupt-Continue load/store Instructions (ICI) and If-Then (IT) instruction) fields to allow continuation of these instructions on return from an interrupt. This xPSR state is expected because the required T (Thumb) bit in the EPSR is set. The SCB_CFSR (value 0x00008200) has two bits set. The BFARVALID bit is set which, according to the debugger, indicates "BFAR holds a valid fault address." The manual says, "BFAR has valid contents." Additionally, the PRECISERR bit is set which, according to the debugger, indicates "a data bus error has occurred, and the PC value stacked for the exception return points to the instruction that caused the fault." The manual says, "a precise data access error has occurred, and the processor has written the faulting address to the BFAR." The SCB_HFSR (value 0x40000000) has the FORCED bit set which, according to the debugger, indicates "forced HardFault." The manual says, "processor has escalated a configurable-priority exception to HardFault." The SCB_BFAR contains the value 0x00000003 which is that address that caused the fault.